﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CustomTypes.Interfaces;
using Mono.Cecil;
//using Microsoft.VisualStudio.Modeling;
using CustomTypes.Mono.CSharpParser;
using System.Collections;

namespace devtm.AutoMapper
{
    
    public abstract class TypeRepository : IDisposable
    {


        //public EventHandler<AssemblyResolveEventArgs> AssemblyResolver;

        protected Dictionary<string, IAssemblyDescriptor> _dic = new Dictionary<string, IAssemblyDescriptor>();
        protected Dictionary<string, IEnumerable<TypeDefinition>> _repository = new Dictionary<string, IEnumerable<TypeDefinition>>();


        [ThreadStatic]
        protected static TypeRepository _instance;
        private int Count = 0;

        protected TypeRepository()
        {
            
        }


        public static T GetInstance<T>(Func<T> newInstance)
            where T : TypeRepository
        {

            if (_instance != null)
            {
                _instance.Count++;
                return _instance as T;
            }

            _instance = newInstance();

            return _instance as T;

        }

        public static T NewInstance<T>(Func<T> newInstance)
            where T : TypeRepository
        {                       
            _instance = newInstance();
            return _instance as T;
        } 

        public void Dispose()
        {
            Dispose(true);
        }

        public void Dispose(bool disposing)
        {

            if (disposing)
            {
                Count--;
            }


            if (_dic != null & Count < 0)
            {
                _dic.Clear();
                _instance = null;
            }

        }

        ~TypeRepository()
        {

            Dispose(false);
            _instance = null;

        }

        //internal TypeDefinition GetType(Microsoft.RestrictedUsage.CSharp.Semantics.CSharpType type)
        //{
        //    return GetType(type.Assembly.Name.Text, type.FullNamespace(), type.Name.Text);
        //}


        public TypeDefinition GetType(string assemblyName, string @namespace, string name)
        {

            IAssemblyDescriptor ass = GetAssembly(assemblyName);

            if (ass != null)
                return ass.GetType(@namespace, name);

            return null;

        } 

        public IEnumerable<TypeDefinition> GetTypes(string assemblyName, HashSet<string> namespaces)
        {

            IAssemblyDescriptor ass = GetAssembly(assemblyName);

            if (ass != null)
                foreach (var item in ass.GetTypes(namespaces))
                    yield return item;

        }

        public IEnumerable<TypeDefinition> GetTypes(string assemblyName)
        {

            IAssemblyDescriptor ass = GetAssembly(assemblyName);

            if (ass != null)
                foreach (var item in ass.GetTypes(null))
                    yield return item;
            
        }

        public IEnumerable<TypeDefinition> GetTypes()
        {
                          
            ResolveAssembly(string.Empty);
            
            foreach (var item in _dic)            
                foreach (var type in item.Value.GetTypes(null))
                    yield return type;
            
        }


        public IEnumerable<TypeDefinition> GetTypes(Predicate<TypeDefinition> predicate)
        {

            ResolveAssembly(string.Empty);

            foreach (var item in _dic)
                foreach (var type in item.Value.GetTypes(predicate))
                    yield return type;

        }

        public IAssemblyDescriptor GetAssembly(string assemblyName)
        {

            string _assemblyName = assemblyName.Split(',')[0];

            IAssemblyDescriptor ass = null;

            if (_dic.ContainsKey(_assemblyName))
                ass = _dic[_assemblyName];
            else
                ass = ResolveAssembly(_assemblyName);

            return ass;
        }

        public IEnumerable<IAssemblyDescriptor> GetAssemblies()
        {

            ResolveAssembly("");

            foreach (var item in _dic)            
                yield return item.Value;            
            
        }


        public bool Contains(string assemblyName)
        {
            string _assemblyName = assemblyName.Split(',')[0];
            return _dic.ContainsKey(_assemblyName);
        }


        public abstract IAssemblyDescriptor ResolveAssembly(string assemblyName);

        public List<String> List { get; set; }
        public Hashtable Paths { get; set; }


        public void RegisterFilter(string name, IEnumerable<TypeDefinition> list)
        {
            _repository.Add(name, list);
        }

        public bool GetFilter(string name, out IEnumerable<TypeDefinition> list)
        {

            if (_repository.ContainsKey(name))
            {
                list = _repository[name];
                return true;
            }

            list = null;
            return false;

        }

    }
}
